/**
 ****************************************************************************************************
 * @file        ov7670.h
 * @author      正点原子团队(ALIENTEK)
 * @version     V1.0
 * @date        2022-06-21
 * @brief       OV7670模块驱动代码
 * @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:正点原子 探索者 F407开发板
 * 在线视频:www.yuanzige.com
 * 技术论坛:www.openedv.com
 * 公司网址:www.alientek.com
 * 购买地址:openedv.taobao.com
 *
 ****************************************************************************************************
 */

#ifndef __OV7670_H
#define __OV7670_H

#include "sys.h"
#include "bsp_common.h"

/* 定义使用DCMI接口或直接GPIO驱动 */
#define OV7670_USING_DCMI                   1

#if (OV7670_USING_DCMI == 0)
/* 定义是否使用同一GPIO端口连接OV7670的D0~D7数据引脚 */
#define OV7670_DATA_PIN_IN_SAME_GPIO_PORT   0
#endif

#if (OV7670_USING_DCMI == 0)
#if (OV7670_DATA_PIN_IN_SAME_GPIO_PORT != 0)
/* 连接OV7670模块D0~D7的GPIO端口 */
#define OV7670_DATE_GPIO_PORT  GPIOC
/* 一次性读取连接至OV7670的D0~D7的GPIO引脚数据的掩码 */
#define OV7670_DATA_READ_MASK  0x00FF
#endif
#endif

#if (OV7670_USING_DCMI == 0)
/* 传输图像数据的DMA相关定义 */
#define OV7670_DMA_INTERFACE                DMA2_Stream0
#define OV7670_DMA_CHANNEL                  DMA_CHANNEL_0
#define OV7670_DMA_CLK_ENABLE()             do{ __HAL_RCC_DMA2_CLK_ENABLE(); }while(0)
#endif

/* 引脚定义 */
#if (OV7670_USING_DCMI == 0)
#define OV7670_VSYNC_GPIO_PORT              GPIOB
#define OV7670_VSYNC_GPIO_PIN               GPIO_PIN_7
#define OV7670_VSYNC_GPIO_CLK_ENABLE()      do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)
#define OV7670_HREF_GPIO_PORT               GPIOA
#define OV7670_HREF_GPIO_PIN                GPIO_PIN_4
#define OV7670_HREF_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
#define OV7670_D0_GPIO_PORT                 GPIOC
#define OV7670_D0_GPIO_PIN                  GPIO_PIN_6
#define OV7670_D0_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
#define OV7670_D1_GPIO_PORT                 GPIOC
#define OV7670_D1_GPIO_PIN                  GPIO_PIN_7
#define OV7670_D1_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
#define OV7670_D2_GPIO_PORT                 GPIOC
#define OV7670_D2_GPIO_PIN                  GPIO_PIN_8
#define OV7670_D2_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
#define OV7670_D3_GPIO_PORT                 GPIOC
#define OV7670_D3_GPIO_PIN                  GPIO_PIN_9
#define OV7670_D3_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
#define OV7670_D4_GPIO_PORT                 GPIOC
#define OV7670_D4_GPIO_PIN                  GPIO_PIN_11
#define OV7670_D4_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
#define OV7670_D5_GPIO_PORT                 GPIOB
#define OV7670_D5_GPIO_PIN                  GPIO_PIN_6
#define OV7670_D5_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)
#define OV7670_D6_GPIO_PORT                 GPIOE
#define OV7670_D6_GPIO_PIN                  GPIO_PIN_5
#define OV7670_D6_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)
#define OV7670_D7_GPIO_PORT                 GPIOE
#define OV7670_D7_GPIO_PIN                  GPIO_PIN_6
#define OV7670_D7_GPIO_CLK_ENABLE()         do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)
#define OV7670_PCLK_GPIO_PORT               GPIOA
#define OV7670_PCLK_GPIO_PIN                GPIO_PIN_6
#define OV7670_PCLK_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)
#endif
#define OV7670_RST_GPIO_PORT                GPIOG
#define OV7670_RST_GPIO_PIN                 GPIO_PIN_15
#define OV7670_RST_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)
#define OV7670_SGM_GPIO_PORT                GPIOG
#define OV7670_SGM_GPIO_PIN                 GPIO_PIN_9
#define OV7670_SGM_GPIO_CLK_ENABLE()        do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)

/* IO操作 */
#define OV7670_RST(x)                       do{ x ?                                                                                     \
                                                    HAL_GPIO_WritePin(OV7670_RST_GPIO_PORT, OV7670_RST_GPIO_PIN, GPIO_PIN_SET) :    \
                                                    HAL_GPIO_WritePin(OV7670_RST_GPIO_PORT, OV7670_RST_GPIO_PIN, GPIO_PIN_RESET);   \
                                                }while(0)
#define OV7670_SGM(x)                       do{ x ?                                                                                     \
                                                    HAL_GPIO_WritePin(OV7670_SGM_GPIO_PORT, OV7670_SGM_GPIO_PIN, GPIO_PIN_SET) :    \
                                                    HAL_GPIO_WritePin(OV7670_SGM_GPIO_PORT, OV7670_SGM_GPIO_PIN, GPIO_PIN_RESET);   \
                                                }while(0)
#if (OV7670_USING_DCMI == 0)
#define OV7670_READ_VSYNC()                 (OV7670_VSYNC_GPIO_PORT->IDR & OV7670_VSYNC_GPIO_PIN)
#define OV7670_READ_HREF()                  (OV7670_HREF_GPIO_PORT->IDR & OV7670_HREF_GPIO_PIN)
#define OV7670_READ_PCLK()                  (OV7670_PCLK_GPIO_PORT->IDR & OV7670_PCLK_GPIO_PIN)
#endif

/* OV7670 SCCB通讯地址 */
#define OV7670_SCCB_ADDR                    0x21

/* OV7670模块在不同输出模式下的最大输出分辨率 */
#define OV7670_VGA_WIDTH_MAX                640
#define OV7670_VGA_HEIGHT_MAX               480
#define OV7670_QVGA_WIDTH_MAX               320
#define OV7670_QVGA_HEIGHT_MAX              240
#define OV7670_QCIF_WIDTH_MAX               176
#define OV7670_QCIF_HEIGHT_MAX              144

#define OV7670_WIDTH_CROPED                 240
#define OV7670_HEIGHT_CROPED                240

#define OV7670_WIDTH_SRC                   (OV7670_QVGA_WIDTH_MAX)
#define OV7670_HEIGHT_SRC                  (OV7670_QVGA_HEIGHT_MAX)
#ifdef ENABLE_DCMI_CROP
#define OV7670_WIDTH                       (OV7670_WIDTH_CROPED)
#define OV7670_HEIGHT                      (OV7670_HEIGHT_CROPED)
#else
#define OV7670_WIDTH                       (OV7670_QVGA_WIDTH_MAX)
#define OV7670_HEIGHT                      (OV7670_QVGA_HEIGHT_MAX)
#endif

/* OV7670模块灯光模式枚举 */
typedef enum
{
    OV7670_LIGHT_MODE_AUTO = 0x00,          /* Auto */
    OV7670_LIGHT_MODE_SUNNY,                /* Sunny */
    OV7670_LIGHT_MODE_CLOUDY,               /* Cloudy */
    OV7670_LIGHT_MODE_OFFICE,               /* Office */
    OV7670_LIGHT_MODE_HOME,                 /* Home */
    OV7670_LIGHT_MODE_NIGHT,                /* Night */
} ov7670_light_mode_t;

/* OV7670模块色彩饱和度枚举 */
typedef enum
{
    OV7670_COLOR_SATURATION_0 = 0x00,       /* +4 */
    OV7670_COLOR_SATURATION_1,              /* +3 */
    OV7670_COLOR_SATURATION_2,              /* +2 */
    OV7670_COLOR_SATURATION_3,              /* +1 */
    OV7670_COLOR_SATURATION_4,              /* 0 */
    OV7670_COLOR_SATURATION_5,              /* -1 */
    OV7670_COLOR_SATURATION_6,              /* -2 */
    OV7670_COLOR_SATURATION_7,              /* -3 */
    OV7670_COLOR_SATURATION_8,              /* -4 */
} ov7670_color_saturation_t;

/* OV7670模块亮度枚举 */
typedef enum
{
    OV7670_BRIGHTNESS_0 = 0x00,             /* +4 */
    OV7670_BRIGHTNESS_1,                    /* +3 */
    OV7670_BRIGHTNESS_2,                    /* +2 */
    OV7670_BRIGHTNESS_3,                    /* +1 */
    OV7670_BRIGHTNESS_4,                    /* 0 */
    OV7670_BRIGHTNESS_5,                    /* -1 */
    OV7670_BRIGHTNESS_6,                    /* -2 */
    OV7670_BRIGHTNESS_7,                    /* -3 */
    OV7670_BRIGHTNESS_8,                    /* -4 */
} ov7670_brightness_t;

/* OV7670模块对比度枚举 */
typedef enum
{
    OV7670_CONTRAST_0 = 0x00,                /* +4 */
    OV7670_CONTRAST_1 ,                      /* +3 */
    OV7670_CONTRAST_2 ,                      /* +2 */
    OV7670_CONTRAST_3 ,                      /* +1 */
    OV7670_CONTRAST_4 ,                      /*  0 */
    OV7670_CONTRAST_5 ,                      /* -1 */
    OV7670_CONTRAST_6 ,                      /* -2 */
    OV7670_CONTRAST_7 ,                      /* -3 */
    OV7670_CONTRAST_8 ,                      /* -4 */
} ov7670_contrast_t;

/* OV7670模块特殊效果枚举 */
typedef enum
{
    OV7670_SPECIAL_EFFECT_NORMAL = 0x00,    /* Normal */
    OV7670_SPECIAL_EFFECT_BW,               /* B&W */
    OV7670_SPECIAL_EFFECT_BLUISH,           /* Bluish */
    OV7670_SPECIAL_EFFECT_SEPIA,            /* Sepia */
    OV7670_SPECIAL_EFFECT_REDISH,           /* Redish */
    OV7670_SPECIAL_EFFECT_GREENISH,         /* Greenish */
    OV7670_SPECIAL_EFFECT_NEGATIVE,         /* Negative */
} ov7670_special_effect_t;

/* OV7670模块输出模式枚举 */
typedef enum
{
    OV7670_OUTPUT_MODE_VGA = 0x00,          /* VGA */
    OV7670_OUTPUT_MODE_QVGA,                /* QVGA */
} ov7670_output_mode_t;

enum {
    OV7670_GET_TYPE_BUFFER_8B = 0,
    OV7670_GET_TYPE_BUFFER_16B,
    OV7670_GET_TYPE_BUFFER_32B
};

enum {
    OV7670_GET_TYPE_BUFFER_NOINC = 0,
    OV7670_GET_TYPE_BUFFER_INC
};

enum {
    OV7670_GET_TYPE_BUFFER_LINE = 0,
    OV7670_GET_TYPE_BUFFER_FRAME
};

/* OV7670获取帧数据方式枚举 */
typedef struct {
    uint8_t bufferLength : 2;
    uint8_t bufferInc : 1;
    uint8_t bufferType : 1;
}ov7670_get_type_t;

/* 错误代码 */
#define OV7670_EOK      0   /* 没有错误 */
#define OV7670_ERROR    1   /* 错误 */
#define OV7670_EINVAL   2   /* 非法参数 */
#define OV7670_ENOMEM   3   /* 内存不足 */
#define OV7670_EEMPTY   4   /* 资源为空 */

/* 操作函数 */
uint8_t OV7670_Init(void);                                                                              /* 初始化OV7670模块 */
uint8_t ov7670_set_light_mode(ov7670_light_mode_t mode);                                            /* 设置OV7670模块灯光模式 */
uint8_t ov7670_set_color_saturation(uint8_t saturation);                          /* 设置OV7670模块色彩饱和度 */
uint8_t ov7670_set_brightness(ov7670_brightness_t brightness);                                      /* 设置OV7670模块亮度 */
uint8_t ov7670_set_contrast(ov7670_contrast_t contrast);                                            /* 设置OV7670模块对比度 */
uint8_t ov7670_set_special_effect(ov7670_special_effect_t effect);                                  /* 设置OV7670模块特殊效果 */
uint8_t ov7670_set_output(uint16_t width, uint16_t height, ov7670_output_mode_t mode);              /* 设置OV7670模块输出模式 */
uint8_t ov7670_set_output_speed(uint8_t clk_rc);                                                        /* 设置OV7670模块时钟预分频系数 */
void ov7670_colorbar_enable(void);                                                                      /* 开启OV7670模块彩条测试 */
void ov7670_colorbar_disable(void);                                                                     /* 关闭OV7670模块彩条测试 */
uint16_t ov7670_get_output_width(void);                                                                 /* 获取OV7670模块输出图像宽度 */
uint16_t ov7670_get_output_height(void);                                                                /* 获取OV7670模块输出图像高度 */
uint8_t OV7670GetFrame(uint32_t dts_addr, ov7670_get_type_t type, void (*before_transfer)(void)); /* 获取OV7670模块输出的一帧图像数据 */

#endif
